package com.sdk.auth.filter;


import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.json.JSONUtil;
import com.sdk.auth.aop.SensitiveKeyProperties;
import com.sdk.auth.entity.BaseReq;
import com.sdk.auth.enums.ServerUrlEnum;
import com.sdk.auth.exception.AuthException;
import com.sdk.auth.utils.AESUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Priority;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * @author wst
 * @date 2023/7/5
 * 参数处理
 */
@Slf4j
@Priority(2)
@WebFilter(filterName = "CachedBodyFilter", urlPatterns = {"/*"})
public class CachedBodyFilter extends OncePerRequestFilter {

    @Resource
    private SensitiveKeyProperties sensitiveKeyProperties;
    @Resource
    private HandlerExceptionResolver handlerExceptionResolver;

    @PostConstruct
    public void init() {
        log.info("初始化 Servlet Filter: {}", this.getClass().getName());
    }

    @Override
    protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
        try {
            // 复制一份参数
            log.debug("CachedBodyRequestWrapper");
            CachedBodyRequestWrapper paramsRequestWrapper = new CachedBodyRequestWrapper(servletRequest);
            //根据头部入参，如果传了RSA加密后的AES密钥，则对请求体做解密，并覆盖原来请求体
            aesBody(servletRequest,paramsRequestWrapper);

            filterChain.doFilter(paramsRequestWrapper, servletResponse);
        } catch (AuthException e1) {
            //交给全局异常处理类处理
            handlerExceptionResolver.resolveException(servletRequest, servletResponse, null, e1);
        } catch (Exception e) {
            log.error("转换请求异常：{}", e.getMessage(), e);
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        // 处理 json、parameterMap 请求内容
        return !StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE) && MapUtil.isEmpty(request.getParameterMap());
    }

    @Override
    public void destroy() {
        log.info("销毁 Servlet Filter: {}", this.getClass().getName());
    }

    private void aesBody(HttpServletRequest servletRequest, CachedBodyRequestWrapper paramsRequestWrapper) {
        try {
            //只处理有body的
            if(paramsRequestWrapper.getCachedBody().length == 0){
                return;
            }
            //请求地址判断，拦截固定开头的地址
            if(!ServerUrlEnum.check(servletRequest.getRequestURI())){
                return;
            }
            String body = StrUtil.str(paramsRequestWrapper.getCachedBody(), StandardCharsets.UTF_8);
            //加个日志，防止异常查不到请求日志
            log.info("请求地址:{},加密请求体：{}", servletRequest.getRequestURI(),body);
            BaseReq aesReq = JSONUtil.toBean(body, BaseReq.class);
            //RSA解密得到AES密钥
            RSA rsa = sensitiveKeyProperties.getRsa();
            String aesKey= rsa.decryptStr(aesReq.getKey(), KeyType.PrivateKey);
            //解密获取到body原文
            String newBody = AESUtil.decrypt(aesReq.getData(),aesKey);
            log.info("请求地址:{},请求体：{}", servletRequest.getRequestURI(),newBody);
            //替换原body
            paramsRequestWrapper.setCachedBody(newBody.getBytes(StandardCharsets.UTF_8));
        } catch (AuthException e) {
            throw e;
        }catch (Exception e1){
            log.error("请求体解密异常：{}",servletRequest.getRequestURI(),e1);
            throw new AuthException("请求体解密异常");
        }
    }
}
